﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

/*For observable collection */
using System.Collections.ObjectModel;

/* Threading */
/* Synchronized methods */
using System.Threading;
using System.Runtime.CompilerServices;

using System.IO;

namespace WPF_DataLogger
{
    public enum Rank{
        Good,
        Moderate,
        Unhealthy_1,
        Unhealthy_2,
        Unhealthy_3,
        Hazardous
    }

    public struct Breakpoints{
        public double low;
        public double high;
    }
  
    public struct AQIRange{
        public Rank rank;
        public Breakpoints brk_pts;
    }

    public class GasAQI
    {
        private string sensor_name;
        private List<AQIRange> range_list;

        public GasAQI(){
            sensor_name = "";
            range_list = new List<AQIRange>();
        }

        public string SensorName
        {
            get
            {
                return sensor_name;
            }

            set
            {
                sensor_name = value;
            }
        }

        public void AddRange(AQIRange range)
        {
            range_list.Add(range);
        }

        public void DebugListContent()
        {
            foreach (AQIRange range in range_list)
            {
                Debug("Rank: " + range.rank + " Range: low :" + 
                    range.brk_pts.low + " High: " + range.brk_pts.high, 1);
            }
        }

        public Breakpoints GetAQIBreakPoints(double ppm_val)
        {
            int idx = 0;

            for (idx = 0; idx < range_list.Count - 1; idx++)
            {
                if (IsInRange(ppm_val, range_list[idx].brk_pts.low,
                    range_list[idx].brk_pts.high))
                {
                    return range_list[idx].brk_pts;
                }
            }

            return range_list[range_list.Count - 1].brk_pts;
        }

        public int GetAQILowBound(Rank rank)
        {
            switch (rank)
            {
                case Rank.Good:
                    return 0;
                case Rank.Moderate:
                    return 51;
                case Rank.Unhealthy_1:
                    return 101;
                case Rank.Unhealthy_2:
                    return 151;
                case Rank.Unhealthy_3:
                    return 201;
                case Rank.Hazardous:
                    return 301;
                default:
                    return 333;
            }
        }

        public int GetAQIHighBound(Rank rank)
        {
            switch (rank)
            {
                case Rank.Good:
                    return 50;
                case Rank.Moderate:
                    return 100;
                case Rank.Unhealthy_1:
                    return 150;
                case Rank.Unhealthy_2:
                    return 200;
                case Rank.Unhealthy_3:
                    return 300;
                case Rank.Hazardous:
                    return 400;
                default:
                    return 500;
            }
        }

        public Rank GetRank(double ppm_val)
        {
            int idx = 0;

            for (idx = 0; idx < range_list.Count - 1; idx++)
            {
                if (IsInRange(ppm_val, range_list[idx].brk_pts.low, 
                    range_list[idx].brk_pts.high))
                {
                    return range_list[idx].rank;
                }
            }

            return Rank.Hazardous;
        }

        private bool IsInRange(double value, double low_bound, double high_bound)
        {
            return (value >= low_bound && value <= high_bound);
        }

        public void Clear()
        {
            range_list.Clear();
        }

        public void Debug(string info, int lvl)
        {
            switch (lvl)
            {
                case 0:
                case 1:
                    System.Console.WriteLine(info);
                    break;
            }
        }
    }

    class AQICalculator
    {
        private FileStream _aqi_file;
        private Dictionary<string, GasAQI> _aqi_table;
        private const string file_name = ".\\aqi.ini";

        public AQICalculator(){
            _aqi_table = new Dictionary<string, GasAQI>();

            if (!System.IO.File.Exists(file_name))
            {
                
                throw new FileNotFoundException("AQI calc: " + file_name + " not found.");
            }
            else
            {
                _aqi_file = new FileStream(file_name, FileMode.Open);
            }

        }

        public void CreateLookupTable()
        {
            
            StreamReader stream = new StreamReader(_aqi_file);
            string next_line = "";

            while ((next_line = stream.ReadLine()) != null)
            {
                if (next_line == "--") //Start of a new sensor
                {
                    GasAQI new_entry = CreateAQITableEntry(stream);
                    Debug("name: " + new_entry.SensorName, 1); 
                    new_entry.DebugListContent();

                    _aqi_table.Add(new_entry.SensorName, new_entry);
                }
            }
        }

        public int GetAQI(string gas_name, double ppm_value)
        {
            //Wrong gas name
            if(!_aqi_table.ContainsKey(gas_name))
                return 999;
            GasAQI aqi_list = _aqi_table[gas_name];

            Rank rank = aqi_list.GetRank(ppm_value);

            int low_bound = aqi_list.GetAQILowBound(rank);
            int high_bound = aqi_list.GetAQIHighBound(rank);

            Breakpoints brkp = aqi_list.GetAQIBreakPoints(ppm_value);
            

            return ComputeAQI(ppm_value,
                              brkp.high,
                              brkp.low,
                              high_bound,
                              low_bound);
        }

        private int ComputeAQI(double ppm, double h_bp, double l_bp, int h_bd, int l_bd)
        {
            double tmp;
            double k;
            
            tmp = ppm - l_bp;
            k = (h_bd - l_bd) * 1.0 / (h_bp - l_bp);

            return (int)(tmp * k + l_bd);
        }

        public void Cleanup()
        {
            _aqi_table.Clear();
            _aqi_file.Close();
        }

        public void Reset()
        {
            _aqi_table.Clear();
            _aqi_file.Seek(0, SeekOrigin.Begin);
        }

        private GasAQI CreateAQITableEntry(StreamReader s)
        {
            GasAQI new_entry = new GasAQI();
            
            string next_line = "";

            //First get sensor name;
            new_entry.SensorName = s.ReadLine();

            next_line = s.ReadLine();
            while (next_line != "End" && next_line != null)
            {
                //StringBuilder output = new StringBuilder();
                AQIRange new_range = new AQIRange();
                string[] substrings = next_line.Split(' ');

                new_range.rank = GetRank(substrings[0]);
                new_range.brk_pts.low = Convert.ToDouble(substrings[1]);
                new_range.brk_pts.high = Convert.ToDouble(substrings[2]);

                new_entry.AddRange(new_range);
                next_line = s.ReadLine();
            }

            return new_entry;
        }

        private bool IsRank(string value)
        {
            string lower_case = value.ToLower();
            if (lower_case == "good" || lower_case == "moderate" || lower_case == "unhealthy_1" ||
               lower_case == "unhealthy_2" || lower_case == "unhealthy_3" || lower_case == "Hazardous"
                )
            {
                return true;
            }
            return false;
        }

        private Rank GetRank(string value)
        {
            string lower_case = value.ToLower();
            if (lower_case == "good"){
                return Rank.Good;   
            }else if(lower_case == "moderate") {
                return Rank.Moderate;   
            }else if(lower_case == "unhealthy_1") {
                return Rank.Unhealthy_1;   
            }else if (lower_case == "unhealthy_2"){
                return Rank.Unhealthy_2; 
            }else if(lower_case == "unhealthy_3"){
                return Rank.Unhealthy_3;   
            }

            return Rank.Hazardous;
               
        }

        public void Debug(string info, int lvl)
        {
            switch (lvl)
            {
                case 0:
                case 1:
                    System.Console.WriteLine(info);
                    break;
            }
        }
       
    }

}
